﻿
namespace HSS.ComponentModel
{
	#region Using Directives
	using System;
	using System.ComponentModel;
	using System.Linq.Expressions;
	using System.Runtime.Serialization;
	#endregion

	#region NotifiableObject
	/// <summary>
	/// A base implementation of <see cref="INotifiableObject"/>.
	/// </summary>
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Notifiable")]
	public abstract class NotifiableObject : INotifiableObject, INotifyPropertyChanging, INotifyPropertyChanged
	{
		#region Constructors
		/// <summary>
		/// Constructor
		/// </summary>
		protected NotifiableObject()
		{

		}
		#endregion

		#region Deserialization
		/// <summary>
		/// Not intended for direct use.
		/// </summary>
		/// <param name="context"></param>
		[OnDeserializing, EditorBrowsable(EditorBrowsableState.Never)]
		private void OnDeserializing(StreamingContext context)
		{
			this._isNotifying = false;
		}
		/// <summary>
		/// Not intended for direct use.
		/// </summary>
		/// <param name="context"></param>
		[OnDeserialized, EditorBrowsable(EditorBrowsableState.Never)]
		private void OnDeserialized(StreamingContext context)
		{
			this._isNotifying = true;
		}
		#endregion

		#region INotifiableObject

		#region Explicit Implementation
		/// <summary>
		/// Gets or sets whether or not Notifications should be sent.
		/// </summary>
		[EditorBrowsable(EditorBrowsableState.Never)]
		bool INotifiableObject.IsNotifying
		{
			get { return this.IsNotifying; }
			set { this.IsNotifying = value; }
		}
		#endregion

		/// <summary>
		/// Gets or sets whether or not Notifications should be sent.
		/// </summary>
		[ReadOnly(true)]
		protected bool IsNotifying
		{
			get { return _isNotifying; }
			set { _isNotifying = value; }
		} bool _isNotifying = true;

		#endregion

		#region INotifyPropertyChanging

		#region Explicit Implementation
		/// <summary>
		/// Occurs when a property is about to change.
		/// </summary>
		event PropertyChangingEventHandler INotifyPropertyChanging.PropertyChanging
		{
			add { this.PropertyChanging += value; }
			remove { this.PropertyChanging -= value; }
		}
		#endregion

		/// <summary>
		/// Occurs when a property is about to change.
		/// </summary>
#if !SILVERLIGHT
		[field: NonSerialized]
#endif
		protected event PropertyChangingEventHandler PropertyChanging;
		/// <summary>
		/// Raises this object's PropertyChanging event.
		/// </summary>
		/// <typeparam name="T">The Type of the changing property.</typeparam>
		/// <param name="expression">The expression that represents the Property thats about to change.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
		protected void OnPropertyChanging<T>(Expression<Func<T>> expression)
		{
			Guard.ArgumentNotNull(expression, "expression");
			var propertyName = expression.GetPropertyName();
			this.OnPropertyChanging(propertyName);
		}
		/// <summary>
		/// Raises this object's PropertyChanging event for each of the properties.
		/// </summary>
		/// <param name="propertyNames">The properties that have a new value.</param>
		protected void OnPropertyChanging(params string[] propertyNames)
		{
			Guard.ArgumentNotNull(propertyNames, "propertyNames");
			foreach (var propertyName in propertyNames)
				this.OnPropertyChanging(propertyName);
		}
		/// <summary>
		/// Raises this object's PropertyChanging event.
		/// </summary>
		/// <param name="propertyName">The name of the Property thats about to change.</param>
		protected void OnPropertyChanging(string propertyName)
		{
			Guard.ArgumentNotNullOrEmptyOrWhitespace(propertyName, "propertyName");
			if (!IsNotifying)
				return;
			var handler = this.PropertyChanging;
			if (null != handler)
				handler(this, new PropertyChangingEventArgs(propertyName));
		}
		#endregion

		#region INotifyPropertyChanged

		#region Explicit Implementation
		/// <summary>
		/// Occurs when a property has changed.
		/// </summary>
		event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
		{
			add { this.PropertyChanged += value; }
			remove { this.PropertyChanged -= value; }
		}
		#endregion

		/// <summary>
		/// Occurs when a property has changed.
		/// </summary>
#if !SILVERLIGHT
		[field: NonSerialized]
#endif
		protected internal event PropertyChangedEventHandler PropertyChanged;
		/// <summary>
		/// Call this method when a Property's value has changed.
		/// </summary>
		/// <typeparam name="T">The Type of the changed property.</typeparam>
		/// <param name="expression">The expression that represents the changed property.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
		protected void OnPropertyChanged<T>(Expression<Func<T>> expression)
		{
			Guard.ArgumentNotNull(expression, "expression");
			var propertyName = expression.GetPropertyName();
			this.OnPropertyChanged(propertyName);
		}
		/// <summary>
		/// Call this method for each of the properties that have changed.
		/// </summary>
		/// <param name="propertyNames">The properties that have a new value.</param>
		protected void OnPropertyChanged(params string[] propertyNames)
		{
			Guard.ArgumentNotNull(propertyNames, "propertyNames");
			foreach (var propertyName in propertyNames)
				this.OnPropertyChanged(propertyName);
		}
		/// <summary>
		/// Call this method when a Property's value has changed.
		/// </summary>
		/// <param name="propertyName">The name of the Property being effected.</param>
		protected void OnPropertyChanged(string propertyName)
		{
			Guard.ArgumentNotNullOrEmpty(propertyName, "propertyName");
			if (!IsNotifying)
				return;
			var handler = this.PropertyChanged;
			if (null != handler)
				handler(this, new PropertyChangedEventArgs(propertyName));
		}
		#endregion
	}
	#endregion
}